package com.telit.common.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.telit.common.exception.ForestBaseException;
import com.telit.common.service.MinioService;
import com.telit.common.service.ParamsCheckService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * @Auther amy
 * @Describe 存储服务工具类
 * @ConditionalOnBean 当MinioClient类存在的时候才会去加载MinioUtils类
 */
@Slf4j
//@Component
public class MinioUtils {

    private static MinioService minioService;

    private static ParamsCheckService paramsCheckService;


    /*@Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        minioService = (MinioService) event.getApplicationContext().getBean("minioService");
        paramsCheckService = (ParamsCheckService) event.getApplicationContext().getBean("paramsCheckService");
        log.info("minioService init [{}],paramsCheckedService init[{}]"
                , JSONUtil.toJsonPrettyStr(minioService)
                , JSONUtil.toJsonPrettyStr(paramsCheckService));
    }*/


    @Autowired
    public MinioUtils(MinioService minioService, ParamsCheckService paramsCheckService) {
        MinioUtils.minioService = minioService;
        MinioUtils.paramsCheckService = paramsCheckService;
    }

    public static void setMinioUtils(MinioService minioService, ParamsCheckService paramsCheckService) {
        MinioUtils.minioService = minioService;
        MinioUtils.paramsCheckService = paramsCheckService;
    }

    /**
     * 上传图片文件,没有指定文件名,默认生成文件名
     * 由于其他地方调用太多，暂且抽离出这个方法
     *
     * @param file
     * @param bucket
     * @param filePrefixName
     * @return
     */
    public static String upload(MultipartFile file, String bucket, String filePrefixName) {
        return uploadDetail(file, bucket, filePrefixName, null);
    }

    /**
     * 上传图片，指定文件名
     *
     * @param file
     * @param bucket
     * @param filePrefixName
     * @param targetName
     * @return
     */
    public static String uploadAssignFileName(MultipartFile file, String bucket, String filePrefixName, String targetName) {
        return uploadDetail(file, bucket, filePrefixName, targetName);
    }

    /**
     * 上传
     *
     * @param file           文件
     * @param bucket         桶名称
     * @param filePrefixName 文件夹名称
     * @return
     */
    private static String uploadDetail(MultipartFile file, String bucket, String filePrefixName, String targetName) {
        baseParameter(bucket, filePrefixName);

        if (file.isEmpty() || file.getSize() == 0) {
            throw new ForestBaseException("上传文件不能为空");
        }
        // 测试判断文件类型
        String fileName = file.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        // 校验文件格式,并组合分类
        String fileDocument = checkFileType(suffix);
        filePrefixName = StringUtils.join(fileDocument, "/", filePrefixName);

        // 对于doc文件上传需要进行html文件生成
        try {
            // 设置content_type
            String contentType = getContentType(fileDocument, suffix);
//            String docUrl = minioService.upload(fileName, file.getInputStream(), file.getSize(), bucket, filePrefixName, file.getContentType(),null);
            return minioService.upload(fileName, file.getInputStream(), file.getSize(), bucket, filePrefixName, contentType, targetName);
        } catch (Exception e) {
            log.error("upload file is failed,exception:{}", e.getMessage());
            throw new ForestBaseException(e.getMessage());
        }
    }

    /**
     * 上传文件doc对应的html文件
     *
     * @return
     */
    public static String uploadDocToHtml(File file, String bucket, String filePrefixName, String targetName) {
        baseParameter(bucket, filePrefixName);

        if (file == null || file.length() == 0) {
            throw new ForestBaseException("上传文件不能为空");
        }
        // 测试判断文件类型
        String fileName = file.getName();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        // 校验文件格式,并组合分类
        String fileDocument = checkFileType(suffix);
        filePrefixName = StringUtils.join(fileDocument, "/", filePrefixName);

        // 设置content_type
        String contentType = getContentType(fileDocument, suffix);
        try {
            return minioService.upload(fileName, new FileInputStream(file), file.length(), bucket, filePrefixName, contentType, targetName);
        } catch (Exception e) {
            log.error("upload file is failed,exception:{}", e.getMessage());
            throw new ForestBaseException(e.getMessage());
        }
    }

    /**
     * 预览
     *
     * @param imgUrl 图片全路径
     * @return
     */
    public static String previewImg(String imgUrl) {
        checkImgUrl(imgUrl);
        log.info("minioServer获取的值：[{}],imgUrl :[{}]", JSONUtil.toJsonPrettyStr(minioService), imgUrl);
        final String[] pathArr = StrUtil.split(imgUrl, StrUtil.SLASH);
        log.info("split imgUrl arr is[{}]", JSONUtil.toJsonPrettyStr(pathArr));
        // return minioService.previewImg(imgUrl.substring(0, imgUrl.indexOf("/")), imgUrl.substring(imgUrl.indexOf("/") + 1));
        return minioService.previewImg(imgUrl.substring(0, imgUrl.indexOf("/")), imgUrl.substring(imgUrl.indexOf("/") + 1));
    }

    /**
     * 删除
     *
     * @param imgUrl 图片全路径，不加上minio服务器的地址
     */
    public static void remove(String imgUrl) {
        checkImgUrl(imgUrl);
        minioService.removeMinio(imgUrl.substring(0, imgUrl.indexOf("/")), imgUrl.substring(imgUrl.indexOf("/") + 1));
    }

    /**
     * 下载
     *
     * @param imgUrl       图片全路径
     * @param originalName 下载文件命名名称
     * @param response
     */
    public static void download(String imgUrl, String originalName, HttpServletResponse response) {
        InputStream file = null;
        try {
            checkImgUrl(imgUrl);
            String objectName = imgUrl.substring(imgUrl.indexOf("/") + 1);
            file = minioService.download(imgUrl.substring(0, imgUrl.indexOf("/")), objectName);
            if (StringUtils.isNotEmpty(originalName)) {
                objectName = originalName;
            }

            // 设置编码,防止乱码
            String filename = URLEncoder.encode(objectName, "UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            // 设置文件名
            response.setHeader("filename", filename);
            // 设置服务器headers白名单，可以让客户端进行访问操作的属性
            response.setHeader("Access-Control-Expose-Headers", "filename");

            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while (file != null && (len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
        } catch (Exception e) {
            log.error("文件下载失败");
        } finally {
            if (file != null) {
                try {
                    file.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 移动上传文件夹
     *
     * @param imgUrl
     * @param newBucket
     * @param newFilePrefixName
     */
    public static String removeToNewFolder(String imgUrl, String newBucket, String newFilePrefixName) {
        List<String> mediaList = JSON.parseArray(imgUrl, String.class);
        List<String> list = new ArrayList<>(mediaList.size());
        mediaList.forEach(e -> {
            InputStream inputStream = null;
            try {
                checkImgUrl(e);
                String objectName = e.substring(e.indexOf("/") + 1);
                inputStream = minioService.download(e.substring(0, e.indexOf("/")), objectName);
                MultipartFile file = new MockMultipartFile(objectName.split("/")[2], objectName.split("/")[2], MediaType.MULTIPART_FORM_DATA_VALUE, inputStream);
                String newUrl = upload(file, newBucket, newFilePrefixName);
                remove(e);
                inputStream.close();
                list.add(newUrl);
            } catch (Exception exception) {
                log.error("移动文件夹失败！");
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }
        });
        return JSON.toJSONString(list);
    }


    public static String checkFileType(String suffix) {
        String suffixName = paramsCheckService.checkFieType(suffix);

        if (StringUtils.isEmpty(suffixName)) {
            throw new ForestBaseException("不允许上传" + suffix + "类型文件");
        }
        return suffixName;
    }

    /**
     * 校验图片地址路径
     */
    private static void checkImgUrl(String imgUrl) {
        if (StringUtils.isBlank(imgUrl)) {
            throw new ForestBaseException("图片地址不能为空");
        }
        if (!imgUrl.contains("/")) {
            throw new ForestBaseException("图片地址不符合规范");
        }
    }

    /**
     * 设置content_type,默认的type都是application/x-download(下载格式)
     *
     * @return
     */
    private static String getContentType(String fileDocument, String fileSuffix) {
        String contentType = StringUtils.EMPTY;
        if (("image").equals(fileDocument) || ("audio").equals(fileDocument) || ("video").equals(fileDocument)) {
            contentType = fileDocument + "/" + fileSuffix;
        }
        // pdf格式
        if ("pdf".equals(fileSuffix)) {
            contentType = "application/pdf";
        } else if ("doc".equals(fileSuffix) || "docx".equals(fileSuffix)) {
            contentType = "application/x-docx";
        } else if ("html".equals(fileSuffix)) {
            contentType = "text/html";
        }
        return contentType;
    }

    /**
     * 基础参数校验
     */
    private static void baseParameter(String bucket, String filePrefixName) {

        if (StringUtils.isBlank(bucket)) {
            throw new ForestBaseException("bucket 桶名称不能为空");
        }
        if (!RegexUtils.match(RegexUtils.STRING_LETTER_LOWER_NUMBER, bucket)) {
            throw new ForestBaseException("桶名称必须是小写英文、数字、短横线组成");
        }
        if (!RegexUtils.match(RegexUtils.STRING_LETTER_LOWER_NUMBER, filePrefixName)) {
            throw new ForestBaseException("文件夹名称必须是小写英文、数字、短横线组成");
        }
    }

}
